package com.example.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 面向切面编程：第一种
 */
@Slf4j // 日志
//@Aspect // 表明当前类是一个切面类
//@Component // 添加到spring容器中
public class ExecutionAspect1 {

    /**
     * JoinPoint 与 ProceedingJoinPoint
     * `JoinPoint` 只能用于非环绕通知@Before、@After、@AfterReturning、@AfterThrowing
     * `ProceedingJoinPoint` 是它的子接口，专用于 @Around 通知
     * | `getArgs()`      | 获取目标方法的参数
     * | `getTarget()`    | 获取目标对象（即被代理的类）
     * | `getThis()`      | 获取代理对象（AOP 生成的代理类）
     * | `getSignature()` | 获取方法签名（名字、参数等）
     * | `proceed()`      | 执行目标方法（JoinPoint没有，ProceedingJoinPoint独有）
     */

    /**
     * Spring AOP通知类型:
     * | `@Before`         | 方法执行前执行
     * | `@After`          | 方法执行后（无论异常与否）执行
     * | `@AfterReturning` | 方法正常返回后执行
     * | `@AfterThrowing`  | 方法抛异常后执行
     * | `@Around`         | 环绕通知，方法前后都可插入逻辑
     */

    /**
     * Spring AOP通知执行顺序:
     *       ┌────────────────────────────┐
     *       │        @Around（前）        │
     *       └────────────────────────────┘
     *                  ↓
     *       ┌────────────────────────────┐
     *       │        @Before             │
     *       └────────────────────────────┘
     *                  ↓
     *       ┌────────────────────────────┐
     *       │     目标方法执行             │
     *       └────────────────────────────┘
     *                  ↓
     *    ┌────────────┬────────────┐
     *    │ 正常返回     │  抛出异常   │
     *    ↓             ↓
     * @AfterReturning   @AfterThrowing
     *          ↓               ↓
     *         └──────────┬──────────┘
     *                    ↓
     *               @After（最终通知）
     *                    ↓
     *            @Around（后）继续执行
     */

    /**
     * 前置通知:方法执行前执行
     */
    @Before("execution(* com.example.service.impl.StudentServiceImpl.*(..))")
    public void before(JoinPoint joinPoint) {
        System.out.println("获取目标方法的参数：" + Arrays.toString(joinPoint.getArgs()));
        System.out.println("获取目标对象（被代理对象）：" + joinPoint.getTarget());
        System.out.println("获取代理对象（AOP 生成的代理类）：" + joinPoint.getThis());
        System.out.println("获取方法签名（名字、参数等）：" + joinPoint.getSignature());
        log.info("before..");
    }

    /**
     * 后置通知:方法执行后（无论异常与否）执行
     */
    @After("execution(* com.example.service.impl.StudentServiceImpl.*(..))")
    public void after() {
        log.info("after..");
    }

    /**
     * 后置返回通知:方法正常执行返回后执行
     */
    @AfterReturning("execution(* com.example.service.impl.StudentServiceImpl.*(..))")
    public void afterReturning() {
        log.info("afterReturning..");
    }

    /**
     * 后置异常通知:方法执行异常后执行
     */
    @AfterThrowing("execution(* com.example.service.impl.StudentServiceImpl.*(..))")
    public void afterThrowing() {
        log.info("afterThrowing..");
    }

    /**
     * 环绕通知:方法执行前执行，方法执行后执行
     */
    @Around("execution(* com.example.service.impl.StudentServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("获取目标方法的参数：" + Arrays.toString(proceedingJoinPoint.getArgs()));
        System.out.println("获取目标对象（被代理对象）：" + proceedingJoinPoint.getTarget());
        System.out.println("获取代理对象（AOP 生成的代理类）：" + proceedingJoinPoint.getThis());
        System.out.println("获取方法签名（名字、参数等）：" + proceedingJoinPoint.getSignature());
        log.info("around before..");
        Object result = proceedingJoinPoint.proceed();
        System.out.println("执行目标方法：" + result);
        log.info("around after..");
        return result;
    }

}
